#pragma once

#include "CommonToAll.hpp"
#include <stdexcept>
#include <iostream>
#include <mutex>
#include <unistd.h>
#include <sys/mman.h>

using std::cout;
using std::endl;

template <class T>
class ObjectPool
{
public:
    T *New() // 申请一个类型T大小的空间
    {
        T *obj = nullptr;
        if (_freelist != nullptr) // 有可以被重复利用的空间
        {
            // 当_freelist不为空说明当前有被回收的空间，所以这里先使用这些被回收的空间
            void *next = *(void **)_freelist;
            obj = (T *)_freelist;
            _freelist = next; // 头删
        }
        else
        {
            if (_remanent_bytes < sizeof(T)) // 当没有可以重复利用的空间以及_memory的空间不够时再继续开辟一个128kb的空间
            {
                _remanent_bytes = 128 * 1024; // 开辟一个128kb的空间
                _memory = (char *)SystemAlloc(_remanent_bytes >> 13);
                if (_memory == nullptr) // 失败抛异常
                {
                    throw std::bad_alloc();
                }
            }
            obj = (T *)_memory;
            // 当obj的大小小于指针的大小时，就将给obj开辟一个完整的指针的大小，否则就直接开辟obj的大小就行了
            size_t obj_size = sizeof(T) > sizeof(void *) ? sizeof(T) : sizeof(void *);
            _memory += obj_size;         // 将这个指针向后移动一个T类型大小的空间
            _remanent_bytes -= obj_size; // 从可用空间中删除一个T类型大小的空间
        }

        // err：当 _freelist 非空时（有回收内存），代码会从自由链表_freelist取出内存块赋给 obj
        //      但​​未跳过后续的 _memory 分配流程​​，导致 obj 被后续代码覆盖
        // obj = (T *)_memory;
        // // 当obj的大小小于指针的大小时，就将给obj开辟一个完整的指针的大小，否则就直接开辟obj的大小就行了
        // size_t obj_size = sizeof(T) > sizeof(void *) ? sizeof(T) : sizeof(void *);
        // _memory += obj_size;         // 将这个指针向后移动一个T类型大小的空间
        // _remanent_bytes -= obj_size; // 从可用空间中删除一个T类型大小的空间

        new (obj) T; // 用定位new来指定空间来构造对象T，对T进行初始化

        return obj;
    }
    void Delete(T *obj)
    {
        if (obj != nullptr)
            obj->~T();

        // 这里采用了头插的方式
        *(void **)obj = _freelist; // 先让指向obj的指针指向自由链表的头节点或空节点
        _freelist = obj;           // 让头指针_freelist指向新加进来的头节点
    }

private:
    char *_memory = nullptr;    // 用于指向大块内存的指针
    void *_freelist = nullptr;  // 自由链表，将归还的空间连接在一起
    size_t _remanent_bytes = 0; // 剩余空间大小
public:
    std::mutex _pool_mtx;       // 防止在多线程的情况下ThreadCache申请到的是空指针
};

